package chariott.testing;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.collections.MultiMap;

import chariott.configuration.Globals;
import chariott.partition.BinPackingOrder_FF;
import chariott.partition.BinPackingOrder_FF_DEC;
import chariott.partition.LeastUsedOrder;
import chariott.partition.RoundRobinOrder;

/**
 * class to be used by script generator selecting the most cost effective
 * algorithm to partition the given test cases
 * 
 * @author Zaid
 * 
 */
public enum CostEffectiveAlgorithmSelector {
	INSTANCE;
	private final static double M1COST = 0.06;
	private static int numberOfNodes = 0;
	private static double binSize = 0;
	private static double[] costs = null;

	/**
	 * method to return the most suitable algorithm based on test cases
	 * 
	 * @param numOfNodes
	 * @param itemsToRun
	 * @return
	 */
	public static MultiMap getBestAlgorithm(int numOfNodes,
			ArrayList<RunItem> itemsToRun) {

		numberOfNodes = numOfNodes;
		binSize = Double.parseDouble(Globals.BIN_SIZE);
		costs = new double[numOfNodes];

		RoundRobinOrder rr = new RoundRobinOrder(numberOfNodes, itemsToRun);
		// BinPackingOrder_FF_DEC bbfdec = new BinPackingOrder_FF_DEC(
		// numberOfNodes, itemsToRun, binSize);
		// BinPackingOrder_FF bbf = new BinPackingOrder_FF(numberOfNodes,
		// itemsToRun, binSize);
		LeastUsedOrder lu = new LeastUsedOrder(numberOfNodes, itemsToRun);

		// generate runs for each algorithm to test for best candidate.
		MultiMap rrRuns = rr.generateRuns();
		// MultiMap bbfdecRuns = bbfdec.generateRuns();
		// MultiMap bbfRuns = bbf.generateRuns();
		MultiMap luRuns = lu.generateRuns();

		ArrayList<Double> myResults = new ArrayList<Double>();
		// 0 is RR
		myResults.add(getTotalCost(rrRuns));
		// 1 is LU
		myResults.add(getTotalCost(luRuns));
		// 2 is BBFF
		// myResults.add(getTotalCost(bbfRuns));
		// 3 is BBFFDEC
		// myResults.add(getTotalCost(bbfdecRuns));

		// get the minimum value and select the algorithm accordingly
		switch (myResults.indexOf(Collections.min(myResults))) {
		case 0:
			return rrRuns;
		case 1:
			return luRuns;
			// case 2:
			// //return bbfRuns;
			// case 3:
			// return bbfdecRuns;
		default:
			return rrRuns;
		}

	}

	/**
	 * calculate the total cost of using a partioning algorithm.
	 * 
	 * @param map
	 * @return
	 */
	public static double getTotalCost(MultiMap map) {

		double allCosts = 0;

		for (int j = 0; j < map.size(); j++) {
			List myList = (List) map.get(j);
			double totalWeight = 0;
			for (Object o : myList) {
				RunItem myCase = (RunItem) o;
				totalWeight = totalWeight + myCase.getWeight();

			}

			double hoursUsed = Math.ceil(totalWeight / 60);

			double cost = hoursUsed * M1COST;

			costs[j] = cost;

		}

		for (double d : costs)
			allCosts = allCosts + d;

		if (map.size() < numberOfNodes) {
			double discount = (numberOfNodes - map.size()) * M1COST;
			allCosts = allCosts - discount;
		}

		return allCosts;

	}

}